﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace Jack.EntityFrameworkCore
{
    /// <summary>
    /// 条件建立者
    /// [单元表达式主要考用用在数据库字段或是其他集合字段中考用直接返回bool查询的]
    /// [考用考虑，如果一个内存集合中，考用定义一个属性，属性中有逻辑，例如：return str.Lenght==1;这样的可以用到单元运算符。]
    /// </summary>
    public static class PredicateBuilder
    {
        /// <summary>
        /// 单元 true 表达式
        /// </summary>
        /// <typeparam name="T">指定泛型 T</typeparam>
        /// <returns>true</returns>
        public static Expression<Func<T, bool>> True<T>()
        {
            return item => true;
        }

        /// <summary>
        /// 单元 false 表达式
        /// </summary>
        /// <typeparam name="T">指定泛型 T</typeparam>
        /// <returns>false</returns>
        public static Expression<Func<T, bool>> False<T>()
        {
            return item => false;
        }

        /// <summary>
        /// 双元 Or 表达式
        /// </summary>
        /// <typeparam name="T">指定泛型 T</typeparam>
        /// <param name="exprleft">左表达式</param>
        /// <param name="exprright">右表达式</param>
        /// <returns>返回合并表达式</returns>
        public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> exprleft, Expression<Func<T, bool>> exprright)
        {
            var invokedExpr = Expression.Invoke(exprright, exprleft.Parameters);
            return Expression.Lambda<Func<T, bool>>(Expression.OrElse(exprleft.Body, invokedExpr), exprleft.Parameters);
        }

        /// <summary>
        /// 双元 And 表达式
        /// </summary>
        /// <typeparam name="T">指定泛型 T</typeparam>
        /// <param name="exprleft">左表达式</param>
        /// <param name="exprright">右表达式</param>
        /// <returns>返回合并表达式</returns>
        public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> exprleft, Expression<Func<T, bool>> exprright)
        {
            var invokedExpr = Expression.Invoke(exprright, exprleft.Parameters);
            return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(exprleft.Body, invokedExpr), exprleft.Parameters);
        }

        /// <summary>
        /// 转换
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="expressions"></param>
        /// <returns></returns>
        public static Expression<Func<T, bool>> ToExpression<T>(this IList<Expression<Func<T, bool>>> expressions)
        {
            Expression<Func<T, bool>> predicate = null;
            foreach (var expression in expressions)
            {
                predicate = expression.And(expression);
            }
            return predicate;
        }

        /// <summary>
        /// 让EF 支持类似sql中的in语法
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="TValue"></typeparam>
        /// <param name="selector"></param>
        /// <param name="values"></param>
        /// <returns></returns>
        public static Expression<Func<T, bool>> BuildOrCondition<T, TValue>(this Expression<Func<T, TValue>> selector, IEnumerable<TValue> values)
        {
            if (null == selector)
            {
                throw new ArgumentNullException("selector");
            }

            if (null == values)
            {
                throw new ArgumentNullException("values");
            }

            ParameterExpression p = selector.Parameters.Single();

            if (!values.Any())
            {
                return e => false;
            }

            var equals = values.Select(value => (Expression)Expression.Equal(selector.Body, Expression.Constant(value, typeof(TValue))));
            var body = equals.Aggregate<Expression>((accumulate, equal) => Expression.Or(accumulate, equal));
            return Expression.Lambda<Func<T, bool>>(body, p);
        }
    }
}
